/*
 * Copyright Aristool AG Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package org.opprua.tidy.beans;

import java.lang.reflect.Array;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.opprua.tidy.conversion.ChangerBroker;

/**
 * TODO: add Java doc
 * 
 * Prepared for first commit: 15.04.2007
 * 
 * @author Lin Li
 */
class BeanJson {

  public static <T> T fromJSON(Class<T> type, String data) {
    JSONObject o = null;
    try {
      o = new JSONObject(data);
      return from(type, o);
    } catch (JSONException e) {
      throw new Error("Failed to parse JSON string for java conversion"
                  + e.getCause());
    }
  }

  public static <T> T from(Class<T> type, JSONObject o) {
    BeanField[] k = BeanField.getFields(type).values()
                .toArray(new BeanField[0]);
    T res = BeanHandler.stuff(type);
    for (int i = 0; i < k.length; i++) {
      Object d = o.opt(k[i].getName());
      if (d == null) continue;
      Class<?> c = k[i].getType();
      Object r = null;
      if (c.isArray()) {
        if (!(d instanceof JSONArray)) {
          JSONArray a = new JSONArray();
          a.put(d);
          d = a;
        }
        c = c.getComponentType();
        int l = ((JSONArray) d).length();
        r = Array.newInstance(c, l);
        for (int j = 0; j < l; j++) {
          try {
            Array.set(r, j, convert(c, ((JSONArray) d).get(j)));
          } catch (Exception e) {}
        }
      } else {
        r = convert(c, d);
      }
      k[i].set(res, r);
    }
    return res;
  }

  private static Object convert(Class<?> c, Object d) {
    if (d instanceof JSONObject) {
      try {
        Object r = from(c, (JSONObject) d);
        return r;
      } catch (Exception e) {
        return null;
      }
    } else if (d == JSONObject.NULL) {
      return null;
    }
    // Boolean, Number, String, but not JSONArray
    return ChangerBroker.convert(c, d);
  }

  public static String toJSON(Object o) {
    return toJSONObject(o).toString();
  }

  public static JSONObject toJSONObject(Object o) {
    if (o.getClass().isArray()) {
      JSONArray a = new JSONArray();
      int n = Array.getLength(o);
      for (int i = 0; i < n; i++) {
        Object s = Array.get(o, i);
        a.put(convert(s));
      }
    }
    BeanField[] k = BeanField.getFields(o.getClass()).values().toArray(
                new BeanField[0]);
    JSONObject res = new JSONObject();
    for (int i = 0; i < k.length; i++) {
      Object d = k[i].get(o);
      if (d == null) continue;
      Class<?> c = d.getClass();
      Object r = null;
      if (c.isArray()) {
        r = new JSONArray();
        c = c.getComponentType();
        int l = Array.getLength(d);
        for (int j = 0; j < l; j++) {
          ((JSONArray) r).put(convert(Array.get(d, j)));
        }
      } else {
        r = convert(d);
      }
      res.put(k[i].getName(), r);
    }
    return res;
  }

  private static Object convert(Object d) {
    if (d instanceof Boolean) return String.valueOf(d);
    if (ChangerBroker.getDefaultValue(d.getClass()) != null) {
      return ChangerBroker.convert(String.class, d);
    }
    return toJSONObject(d);
  }
}
